package nba2kmodtool.filedesc;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;

import nba2kmodtool.util.BufferUtil;
import nba2kmodtool.util.ByteFileReader;

class N2kModelPartImporter {

	private static final boolean IS_DEBUG = BaseIffFile.IS_DEBUG;
	protected static final float MOD = N2kModelPartReader.COORD_MOD;
	protected static final float BYTE_MOD = N2kModelPartReader.COORD_BYTE_MOD;
	protected static final int IMPORT_ALL = 0;
	protected static final int IMPORT_XYZ = 1;

	protected BaseIffFile iff;
	protected File file;

	protected N2kModelPartImporter(BaseIffFile iff) {
		this.iff = iff;
		this.file = iff.file;
	}

	public void importModel(File modelfile, N2kPackageFile pagFile, int importMode) throws IOException {
		ByteFileReader in = new ByteFileReader(modelfile, "r");
		ByteFileReader out = new ByteFileReader(file, "rw");
		byte[] buff = new byte[4];
		in.read(buff);
		if (!"2KM".equals(new String(buff).substring(0, 3))) {
			in.close();
			out.close();
			throw new IOException("This is not a Valid N2KM File");
		}
		int num = in.read4BytesAsInt();
		int perSize = in.read4BytesAsInt();
		buff = new byte[8];
		if (IS_DEBUG) {
			System.out.println("imp: " + num + ", " + perSize);
		}
		for (int i = 0; i < num; i++) {
			in.read(buff);
			String partName = new String(buff).trim();
			String[] idxStr = partName.split("-");
			int index = Integer.parseInt(idxStr[0]);
			int childIdx = Integer.parseInt(idxStr[1]);
			N2k3DModelPart part = pagFile.models[index];
			N2k3DChildModelPart child = part.childParts[childIdx];
			//import child vertex datas
			this.import3DModelChildVertexData(child, in, out, perSize, part, importMode);
			int faceNum = in.read4BytesAsInt();
			in.skipBytes(6 * faceNum);
		}
		in.close();
		out.close();
	}

	private void import3DModelChildVertexData(N2k3DChildModelPart child, ByteFileReader in, ByteFileReader out,
			int perSize, N2k3DModelPart model, int importMode) throws IOException {
		int vertexNum = in.read4BytesAsInt();
		if (child.vertNum == 0) {
			return;
		}
		if (vertexNum != child.vertNum) {
			in.close();
			out.close();
			throw new IOException(String.format("Model Part is Not Matching\nResource: %d\nImport: %d", child.vertNum,
					vertexNum));
		}
		boolean isMod = false;
		out.seek(model.vertexDataStart + 6);
		int modFlag = out.readBytesAsInt(2);
		if (MOD == modFlag) {
			isMod = true;
			if (IS_DEBUG) {
				System.out.println("IsMod:" + modFlag);
			}
		}
		ByteBuffer buffer = ByteBuffer.allocate(perSize * vertexNum);
		in.read(buffer.array());
		buffer.rewind();
		int modelVersion = model.modelVersion;
		int modelDef1 = model.modelDef1;
		int modelDef2 = model.modelDef2;
		int childVertStart = child.vertStart;
		int perVertexSize = model.perVertexSize;
		int perUVNSize = model.perUVNSize;
		int dataChunkCount = model.dataChunkCount;
		boolean importUV = importMode == IMPORT_ALL;
		//import vertex position data buffer
		long baseVertexDataAddr = model.vertexDataStart + childVertStart * perVertexSize;
		if (dataChunkCount == 1) {
			//one data chunk model
			for (int i = 0; i < vertexNum; i++) {
				buffer.position(i * perSize);
				out.seek(baseVertexDataAddr + i * perVertexSize);
				float x = BufferUtil.readBytesAsFloat(buffer, 4);
				float y = BufferUtil.readBytesAsFloat(buffer, 4);
				float z = BufferUtil.readBytesAsFloat(buffer, 4);
				this.writeVertexCoordinate(out, x, y, z, perVertexSize, isMod);
				if (perSize == 20 && importUV) {
					float u = BufferUtil.readBytesAsFloat(buffer, 4);
					float v = BufferUtil.readBytesAsFloat(buffer, 4);
					this.writeVertexUVCoordinate(out, u, v, perVertexSize, isMod);
				}
			}
		} else {
			//multi data chunk model
			for (int i = 0; i < vertexNum; i++) {
				buffer.position(i * perSize);
				out.seek(baseVertexDataAddr + i * perVertexSize);
				float x = BufferUtil.readBytesAsFloat(buffer, 4);
				float y = BufferUtil.readBytesAsFloat(buffer, 4);
				float z = BufferUtil.readBytesAsFloat(buffer, 4);
				this.writeVertexCoordinate(out, x, y, z, perVertexSize, isMod);
			}
			if (perSize == 20 && importUV) {
				//import uvn position datas
				int uvnDefType = model.uvnDefType;
				long baseUVNDataAddr = model.uvnDataStart + perUVNSize * childVertStart;
				for (int i = 0; i < vertexNum; i++) {
					buffer.position(i * perSize + 12);
					out.seek(baseUVNDataAddr + i * perUVNSize);
					float u = BufferUtil.readBytesAsFloat(buffer, 4);
					float v = BufferUtil.readBytesAsFloat(buffer, 4);
					this.writeMultiChunkUVCoordinate(out, u, v, perVertexSize, perUVNSize, isMod);
				}
			}
		}
	}

	protected void writeVertexCoordinate(ByteFileReader out, float x, float y, float z, int perVertexSize, boolean isMod)
			throws IOException {
		if (isMod) {
			short tx = (short) (x * MOD);
			short ty = (short) (y * MOD);
			short tz = (short) (z * MOD);
			out.writeShortBigEndin(tx);
			out.writeShortBigEndin(ty);
			out.writeShortBigEndin(tz);
		} else {
			switch (perVertexSize) {
			case 4:
				byte tx = (byte) (x * BYTE_MOD);
				byte ty = (byte) (y * BYTE_MOD);
				byte tz = (byte) (z * BYTE_MOD);
				out.writeShortBigEndin(tx);
				out.writeShortBigEndin(ty);
				out.writeShortBigEndin(tz);
				break;
			default:
				out.writeFloatBigEndin(x);
				out.writeFloatBigEndin(y);
				out.writeFloatBigEndin(z);
			}
		}
	}

	protected void writeVertexUVCoordinate(ByteFileReader out, float u, float v, int perVertexSize, boolean isMod)
			throws IOException {
		short tu, tv;
		switch (perVertexSize) {
		case 56:
			out.skipBytes(4);
			break;
		case 52:
			out.skipBytes(0x18);
			out.writeFloatBigEndin(u);
			out.writeFloatBigEndin(v);
			break;
		case 48:
			out.skipBytes(0x18);
			tu = (short) (u * MOD);
			v = 1F - v;
			tv = (short) (v * MOD);
			out.writeShortBigEndin(tu);
			out.writeShortBigEndin(tv);
			break;
		case 42:
			out.skipBytes(4);
			break;
		case 36:
			if (isMod) {
				out.skipBytes(18);
				tu = (short) (u * MOD);
				v = 1F - v;
				tv = (short) (v * MOD);
				out.writeShortBigEndin(tu);
				out.writeShortBigEndin(tv);
			} else {
				out.skipBytes(8);
				out.writeFloatBigEndin(u);
				out.writeFloatBigEndin(v);
			}
			break;
		case 28:
			tu = (short) (u * MOD);
			v = 1F - v;
			tv = (short) (v * MOD);
			out.writeShortBigEndin(tu);
			out.writeShortBigEndin(tv);
			break;
		case 4:
			break;
		default:
			throw new IOException("Unkown Vertex data Type! " + perVertexSize);
		}
	}

	protected void writeMultiChunkUVCoordinate(ByteFileReader out, float u, float v, int perVertexSize, int perUVNSize,
			boolean isMod) throws IOException {
		switch (perUVNSize) {
		case 40:
			out.skipBytes(0x18);
			out.writeFloatBigEndin(u);
			out.writeFloatBigEndin(v);
			break;
		case 36:
			out.skipBytes(0x18);
			break;
		case 28:
			out.skipBytes(0x10);
			break;
		case 24:
			break;
		case 16:
			out.skipBytes(4);
		case 4:
			break;
		default:
			throw new IOException("Unkown UVN data Type! " + perUVNSize);
		}
		short tu = (short) (u * MOD);
		v = 1F - v;
		short tv = (short) (v * MOD);
		out.writeShortBigEndin(tu);
		out.writeShortBigEndin(tv);
	}
}
